package MiniC.Parser;


import MiniC.ErrorReporter;
import MiniC.Scann.Scanner;
import MiniC.Scann.SourcePos;
import MiniC.Scann.Token;

public class Parser {

    private Scanner scanner;
    private ErrorReporter errorReporter;
    private Token currentToken;
    private int tokenBufferCount;
    private Token[] tokenBuffer;
    private Token lookAhead;
        
    public Parser(Scanner lexer, ErrorReporter reporter) {
    	scanner = lexer;
        errorReporter = reporter;
        tokenBufferCount = 0;
        tokenBuffer = new Token[20];
    }

    // accept() checks whether the current token matches tokenExpected.
    // If so, it fetches the next token.
    // If not, it reports a syntax error.
    void accept (int tokenExpected) throws SyntaxError {
		if (currentToken.kind == tokenExpected) {
		    currentToken = scanner.scan();
		} else {
		    syntaxError("\"%\" expected here", Token.spell(tokenExpected));
		}
    }

    // acceptIt() unconditionally accepts the current token
    // and fetches the next token from the scanner.
    void acceptIt() {
    	
    	if(tokenBufferCount > 0){
    		currentToken = tokenBuffer[tokenBufferCount - 1];
    		tokenBufferCount--;
        }else{
        	currentToken = scanner.scan();
        }
    	
    }

    void syntaxError(String messageTemplate, String tokenQuoted) throws SyntaxError {
		SourcePos pos = currentToken.GetSourcePos();
		errorReporter.reportError(messageTemplate, tokenQuoted, pos);
		throw(new SyntaxError());
    }

    boolean isTypeSpecifier(int token) {
		if(token == Token.VOID ||
	           token == Token.INT  ||
	           token == Token.BOOL ||
	           token == Token.FLOAT) {
		    return true;
		} else {
		    return false;
		}
    }
    
    boolean isTypeOperator(int token) {
		if(token == Token.PLUS ||
	           token == Token.MINUS  ||
	           token == Token.TIMES ||
	           token == Token.DIV) {
		    return true;
		} else {
		    return false;
		}
    }
    
    boolean isTypeLiteral(int token) {
		if( token == Token.INTLITERAL	|| 
			token == Token.FLOATLITERAL	||
			token == Token.BOOLLITERAL	||
			token == Token.STRINGLITERAL ) {
			return true;
		} else {
			return false;
		}
	}
    
	boolean isTypeCompare(int token) {
		if( token == Token.EQ		||
			token == Token.NOTEQ	||
			token == Token.LESSEQ	||
			token == Token.LESS		||
			token == Token.GREATER	||
			token == Token.GREATEREQ ) {
			return true;
		} else {
			return false;
		}
	}

	boolean isTypeExpr(int token){
    	if( token == Token.PLUS	||
			token == Token.MINUS	||
			token == Token.NOT  ||
			token == Token.ID ||
			isTypeLiteral(token) ||
			token == Token.LEFTPAREN)    		
		{
			return true;
		} else {
			return false;
		}
	}
	
	boolean isTypeStmt(int token){
    	if( token == Token.LEFTBRACE	||
			token == Token.IF	||
			token == Token.WHILE ||
			token == Token.FOR ||			
			token == Token.RETURN ||
			token == Token.ID )    		
		{
			return true;
		} else {
			return false;
		}
	}
	
	
	boolean isTypeLogicOperator(int token){
    	if( token == Token.OR	||
			token == Token.AND	||
			token == Token.NOT)
		{
			return true;
		} else {
			return false;
		}
	}
	
	boolean isType(int token){
	//	if( == Token.)		
		return false;
	}
	
	private void insertTokenToBuffer(Token t)
    {
        if(tokenBufferCount == tokenBuffer.length){
            System.out.println("insertTokenToBuffer error: tokenBuffer overflow!");
            System.exit(-1);
        }
        tokenBuffer[tokenBufferCount] = t;
        tokenBufferCount++;
    }

	 private void tokenToStack()
	 {
	     insertTokenToBuffer(currentToken);
	 }
	
    ///////////////////////////////////////////////////////////////////////////////
    //
    // toplevel parse() routine:
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parse() {

	currentToken = scanner.scan(); // get first token from scanner...

	try {
	    parseProgram();
	    	if (currentToken.kind != Token.EOF) {
	    			syntaxError("\"%\" not expected after end of program",
			       currentToken.GetLexeme());
	    	}
	}
	catch (SyntaxError s) {return; /* to be refined in Assignment 3...*/ }
		return;
    }

    
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseProgram():
    //
    // program ::= ( (VOID|INT|BOOL|FLOAT) ID ( FunPart | VarPart ) )*
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parseProgram() throws SyntaxError {
		while (isTypeSpecifier(currentToken.kind)) { // check void, int, bool, float 
	        acceptIt();					// next token
	        accept(Token.ID);				// current token should be equivalent to Token.ID(=identifier). if not, error occur 
			    if(currentToken.kind == Token.LEFTPAREN) { // Indication of '('
			    	parseFunPart();			    	
			    } else {
			    	parseVarPart();
			    }
		}
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseFunPart():
    //
    // FunPart ::= ( "(" ParamsList? ")" CompoundStmt )
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parseFunPart() throws SyntaxError {
        // We already know that the current token is "(".
        // Otherwise use accept() !
        accept(Token.LEFTPAREN);        
        if(isTypeSpecifier(currentToken.kind)){        
        	parseParamsList();					
        }
    	accept(Token.RIGHTPAREN);		// Indication of ')'
		parseCompoundStmt();
		
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseParamsList():
    //
    // ParamsList ::= ParamsDecl ( "," ParamsDecl ) *
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parseParamsList() throws SyntaxError {
	// to be completed by you...
    	parseParamsDecl();    	   	
    	while (currentToken.kind == Token.COMMA) { // check ',' 
            acceptIt();					// next token
            parseParamsDecl();
    	}
    } 
    
    public void parseParamsDecl() throws SyntaxError {
    	parseTypespecifier();
    	//id or id[INTERAL];
    	parseDeclarator();
    }
    
    public void parseDeclarator() throws SyntaxError{
    	accept(Token.ID);
    	if(currentToken.kind == Token.LEFTBRACKET){
    		accept(Token.LEFTBRACKET);
    		accept(Token.INTLITERAL);
    		accept(Token.RIGHTBRACKET);
    	}
    }

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseCompoundStmt():
    //
    // CompoundStmt ::= "{" VariableDefinition* Stmt* "}"
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parseCompoundStmt() throws SyntaxError {
	// to be completed by you...
    	accept(Token.LEFTBRACE);	// current token should be equivalent to '{'. if not, error occur 
    	  	
    	//VariableDefinition*
    	while(isTypeSpecifier(currentToken.kind)){    	
    			parseVariable_def();
    	}
    	//Stmt*
    	while(isTypeStmt(currentToken.kind)){    	
    			parseStmt();
    	}    	
    	
    	accept(Token.RIGHTBRACE);	//next Token 
    } 
    
    public void parseVariable_def() throws SyntaxError{
    	// first, it should become specifier 
    	acceptIt();	//identifier
    	parseInit_decl_list();
    	accept(Token.SEMICOLON);
    }

    public void parseStmt() throws SyntaxError{
    	switch (currentToken.kind) {
	    	case Token.LEFTBRACE :
	    		parseCompoundStmt();
	    		break;
	    	case Token.IF :
	    		parseIf_stmt();
	    		break;
	    	case Token.WHILE:
	    		parseWhile_stmt();
	    		break;
	    	case Token.FOR :
	    		parseFor_stmt();
	    		break;
	    	case Token.RETURN :
	    		accept(Token.RETURN);
	    		if(isTypeExpr(currentToken.kind)){
	    			parseExpr();
	    		}
	    		accept(Token.SEMICOLON);
	    		break;
	    	case Token.ID :
	    		accept(Token.ID);
	    		if(currentToken.kind == Token.ASSIGN){
	    			accept(Token.ASSIGN);
	    			parseExpr();
	    		}else if(currentToken.kind == Token.LEFTBRACKET){
	    			accept(Token.LEFTBRACKET);
	    			parseExpr();
	    			accept(Token.RIGHTBRACKET);
	    			
	    			accept(Token.ASSIGN);
	    			parseExpr();
	    		}else if(currentToken.kind == Token.LEFTPAREN){
	    			parseArgList();
	    		}else{
	    			syntaxError("",
	    				       currentToken.GetLexeme());    
	    		}
	    		accept(Token.SEMICOLON);
	    		break;	    		    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseIf_stmt() throws SyntaxError{
    	accept(Token.IF);
    	accept(Token.LEFTPAREN);

    	parseExpr();
    	accept(Token.RIGHTPAREN);
    	
    	parseStmt();
    	if(currentToken.kind == Token.ELSE){
    		acceptIt();
    		parseStmt();
    	}
    	
    }
    
    public void parseWhile_stmt() throws SyntaxError{
    	accept(Token.WHILE);
    	accept(Token.LEFTPAREN);
    	parseExpr();
    	accept(Token.RIGHTPAREN);
    	parseStmt();
    }
    
    public void parseFor_stmt() throws SyntaxError{
    	accept(Token.FOR);
    	accept(Token.LEFTPAREN);
    	if(currentToken.kind == Token.ID){
    		parseAsgnexpr();
    	}
    	accept(Token.SEMICOLON);
    	
    	if(isTypeExpr(currentToken.kind)){
    		parseExpr();
    	}
    	accept(Token.SEMICOLON);
    	
    	if(currentToken.kind == Token.ID){
    		parseAsgnexpr();
    	}
    	
    	accept(Token.RIGHTPAREN);
    	parseStmt();
    }
    
    public void parseAsgnexpr() throws SyntaxError{
    	accept(Token.ID);
    	accept(Token.ASSIGN);
    	parseExpr();
    }
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseVarPart():
    //
    // VarPart ::= ( "[" INTLITERAL "]" )?  ( "=" initializer ) ? ( "," init_decl)* ";"
    //
    ///////////////////////////////////////////////////////////////////////////////

    public void parseVarPart() throws SyntaxError {
	// to be completed by you...
    	
    	if(currentToken.kind == Token.LEFTBRACKET){
    		accept(Token.LEFTBRACKET);
    		accept(Token.INTLITERAL);
    		accept(Token.RIGHTBRACKET);
    	}
    	
    	if(currentToken.kind == Token.ASSIGN){
    		accept(Token.ASSIGN);
    		parseInitializer();    		
    	}
    	//parseArrayPart();    	
    	while(currentToken.kind == Token.COMMA){
    		accept(Token.COMMA);
    		parseInit_decl(); 	
        }
    	accept(Token.SEMICOLON);
    }    

    
    public void parseTypespecifier()throws SyntaxError{
    	if (isTypeSpecifier(currentToken.kind)) {  // check void, int, bool, float 
        	acceptIt();
    	}else{
    		syntaxError("",
   			       currentToken.GetLexeme());
     	}
    }  
    public void parseInit_decl_list()throws SyntaxError{
    	parseInit_decl();
    	while(currentToken.kind == Token.COMMA){
    		acceptIt();
    		parseInit_decl();    		      	
        }
    }  
    
    public void parseInit_decl()throws SyntaxError{
    	parseDeclarator();
    	if(currentToken.kind == Token.ASSIGN){
    		acceptIt();
    		parseInitializer();
    	}    	
    }
    
    public void parseInitializer()throws SyntaxError{
    	if(currentToken.kind == Token.LEFTBRACE){ // '{'
    		acceptIt();
    		parseExpr();
    			while(currentToken.kind == Token.COMMA){
    				acceptIt();
    				parseExpr();    		      	
    	        }
    		accept(Token.RIGHTBRACE);    		
    	}else{
    		parseExpr();
    	}    	 
    }
    
    public void parseExpr()throws SyntaxError{
    	if(!isTypeExpr(currentToken.kind)){
    		syntaxError("",
    			       currentToken.GetLexeme());
    	}
    	parseOr_expr();
    }
    
    public void parseOr_expr()throws SyntaxError{
    	//and-expr    or    or-expr "||" and-expr

    	switch (currentToken.kind) {
	    	case Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:	    		
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		parseAnd_expr();
	    		while(currentToken.kind == Token.OR){
	    			acceptIt();
	    			parseAnd_expr();
	    		}
	    		break;	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseAnd_expr()throws SyntaxError{
    	// relational-expr   or       and-expr "&&" relational_expr
    	switch (currentToken.kind) {
	    	case Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:	    		
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		parseRelational_expr();
	    		while(currentToken.kind == Token.AND){
	    			acceptIt();
	    			parseRelational_expr();
	    		}
	    		break;	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseRelational_expr()throws SyntaxError{
    	// add_expr   or   6type
    	switch (currentToken.kind) {
	    	case Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:	    		
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		parseAdd_expr();
	    		while(isTypeCompare(currentToken.kind)){
	    			acceptIt();
	    			parseAdd_expr();
	    		}
	    		break;	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseAdd_expr()throws SyntaxError{
    	// parseMulti_expr   or   2type
    	switch (currentToken.kind) {
	    	case Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:	    		
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		parseMult_expr();
	    		while((currentToken.kind == Token.PLUS) || (currentToken.kind == Token.MINUS)){
	    			acceptIt();
	    			parseAdd_expr();
	    		}
	    		break;	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseMult_expr()throws SyntaxError{
    	// unary_expr   or   
	   	switch (currentToken.kind) {
	    	case Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:	    		
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		parseUnary_expr();
	    		while((currentToken.kind == Token.TIMES) || (currentToken.kind == Token.DIV)){
	    			acceptIt();
	    			parseMult_expr();
	    		}
	    		break;	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}   	
    }
    
    public void parseUnary_expr()throws SyntaxError{
    	// primary_expr   or
	   	switch (currentToken.kind) {
	    	case  Token.ID :
	    	case Token.LEFTPAREN :
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:
	    		parsePrimary_expr();
	    		break;
	    	case Token.PLUS :
	    	case Token.MINUS :
	    	case Token.NOT :
	    		acceptIt();
	    		parseUnary_expr();
	    		break;
	    	
		default: syntaxError("",
			       currentToken.GetLexeme());    
		}
    }
    public void parsePrimary_expr()throws SyntaxError{
    	// id   or
    	switch (currentToken.kind) {
	    	case  Token.ID :
	    		accept(Token.ID);
	    		if(currentToken.kind == Token.LEFTPAREN){
	    			parseArgList();
	    		}else if(currentToken.kind== Token.LEFTBRACKET){
	    			accept(Token.LEFTBRACKET);	    			
	    			parseExpr();
	    			accept(Token.RIGHTBRACKET);
	    		}	    		
	    	break;
	    	case Token.LEFTPAREN :
	    		accept(Token.LEFTPAREN);	    		
	    		parseExpr();
	    		accept(Token.RIGHTPAREN);
	    	
	    	break;
	    	case Token.INTLITERAL :
	    	case Token.BOOLLITERAL :
	    	case Token.FLOATLITERAL:
	    	case Token.STRINGLITERAL:
	    		acceptIt();
	    		break;
    	default: syntaxError("",
			       currentToken.GetLexeme());    
    	}
    }
    
    public void parseArgList()throws SyntaxError{
       	accept(Token.LEFTPAREN);
       	if(isTypeExpr(currentToken.kind)){
       		parseArgs();
       	}
    	accept(Token.RIGHTPAREN);
    }
    
    public void parseArgs()throws SyntaxError{
       	parseArg();
       	while(currentToken.kind == Token.COMMA){
			accept(Token.COMMA);
			parseArgs();			
		}
    }
    
    public void parseArg()throws SyntaxError{    	
    	parseExpr();    	
    }
}
